/*
 * COPYRIGHT:	2010 Christopher Smith, licenced under the GNU GPL v2; see COPYING in the top level directory
 * PROJECT:		sdedit
 * FILE:		win32 tc module.cpp
 * PURPOSE:		definition of ntapi module class and frontend to opening ntobjects by type name
 *				support for securable object types exported via the Object Manager (in kernel mode)
 */

namespace Ntapi
{

class NtapiModule : public Module::ModuleObject
{
	auto_SDE_OPTS ModOpts;

public:
	NtapiModule() //ntapi has no options, no specific init for ModOpts
	{}

	PCWSTR ModuleName() override
	{
		return L"Ntapi";
	}

	virtual SDE_OPTS &ModuleOptions() override
	{
		return ModOpts;
	}

private:
	static int ExceptionToOpenStatus(EXCEPTION_POINTERS *ExceptionPointers, Module::OPEN_STATUS &OpenStatus)
	{
		if(ExceptionPointers->ExceptionRecord->ExceptionCode == STATUS_NT_ERROR)
			switch(CodeFromExceptionRecord(ExceptionPointers->ExceptionRecord))
			{
			case STATUS_ACCESS_DENIED:
				OpenStatus |= Module::SECURITY_FAILURE | Module::OBJECT_OPEN_FAIL;
				break;

			case STATUS_OBJECT_NAME_NOT_FOUND:
			case STATUS_BAD_NETWORK_PATH:
			case STATUS_DEVICE_DOES_NOT_EXIST:
			case STATUS_NETWORK_NAME_DELETED:
			case STATUS_NO_SUCH_FILE:
			case STATUS_INVALID_SYSTEM_SERVICE:
				OpenStatus |= Module::OBJECT_NAME_NOTFOUND;
				break;

			case STATUS_OBJECT_NAME_INVALID:
			case STATUS_OBJECT_PATH_INVALID:
			case STATUS_OBJECT_PATH_SYNTAX_BAD:
			case STATUS_INVALID_COMPUTER_NAME:
				OpenStatus |= Module::OBJECT_NAME_INVALID;
				break;

			default:
				OpenStatus |= Module::OBJECT_OPEN_FAIL;
				break;
			}

		OpenStatus |= Module::COMPLETE_FAILURE;

		return EXCEPTION_CONTINUE_SEARCH; //just passing through
	}

	void OpenResourcesGuarded(Module::OPEN_STATUS &OpenStatus, SDE_GENERIC_OPTS const &GOpts, Module::ModuleResourceDest &Res, UCHAR Privileges, BOOLEAN ObjectWanted)
	{
		if(!ObjectWanted)
		{
			OpenStatus |= Module::MODULE_IRRELEVANT;
			return;
		}

		PCWSTR Name = GOpts.ObjName;
		PCWSTR Type = GOpts.ObjType;
		BOOL CaseInsensitive = GOpts.CaseSensitiveSpecified ? GOpts.CaseSensitive : FALSE;

		OBJECT_ATTRIBUTES Oa;
		UNICODE_STRING Name_U;
		ULONG_PTR NameAsNumber = _wtoiptr(Name);

		RtlInitUnicodeString(&Name_U, const_cast<PWSTR>(Name));

		RtlZeroLengthedStruct(Oa);
		Oa.ObjectName = &Name_U;
		Oa.Attributes = CaseInsensitive ? OBJ_CASE_INSENSITIVE : 0;

		SECURITY_QUALITY_OF_SERVICE SecQos;
		RtlZeroLengthedStruct(SecQos);
		SecQos.ImpersonationLevel = SecurityAnonymous;
		Oa.SecurityQualityOfService = &SecQos;

		ACCESS_MASK StandardAccessRequested = MAXIMUM_ALLOWED | StandardPrivilegedRights(Privileges);
		ACCESS_MASK StorageAccessRequested = MAXIMUM_ALLOWED | StoragePrivilegedRights(Privileges);
		ULONG FileOptions = Privileges & (PRIV_BACKUP | PRIV_RESTORE) ? FILE_OPEN_FOR_BACKUP_INTENT : 0;
		ULONG KeyOptions = Privileges & (PRIV_BACKUP | PRIV_RESTORE) ? REG_OPTION_BACKUP_RESTORE : 0;

		HANDLE Handle = NULL;
		PCWSTR const *SpecificHeaders = NULL;
		NTSTATUS Status = STATUS_NOT_SUPPORTED;
		IO_STATUS_BLOCK IoSb;

		ULONG ExtraSdFlags = 0;

		ExtraSdFlags |= Privileges & PRIV_RELABEL ? SD_FLAG_LABEL_SET_ANY : 0;

		if(GOpts.Host)
			ExtraSdFlags |= Netapi::AreLabelsSupportedRemotely(GOpts.Host);
		else
			ExtraSdFlags |= AreLabelsSupportedLocally() ? SD_FLAG_LABEL_SUPPORT : 0;

		if(!Type)
		{
			OpenStatus |= Module::TYPE_NOT_SUPPORTED;
			return;
		}
		else if(_wcsicmp(Type, L"Key") == 0)
		{
			RaiseOnNtError(NtOpenKeyWithOptions(&Handle, StorageAccessRequested, &Oa, KeyOptions));
			Res.Object(new Key(Handle));
			OpenStatus |= Module::OBJECT_OPENED;
			return;
		}
		else if(_wcsicmp(Type, L"File") == 0)
		{
			RaiseOnNtError(AntiLockNtOpenFile(&Handle, &Oa, &IoSb, Privileges, FileOptions));
			Res.Object(new File(Handle));
			OpenStatus |= Module::OBJECT_OPENED;
			return;
		}
		else if(_wcsicmp(Type, L"Token") == 0)
			Status = OpenProcessForToken(&Handle, StandardAccessRequested, Oa, NameAsNumber),
			SpecificHeaders = Specific::Token,
			ExtraSdFlags = SD_FLAG_NO_DELETE;
		else if(_wcsicmp(Type, L"Process") == 0)
			Status = OpenNtProcess(&Handle, StandardAccessRequested, Oa, NameAsNumber),
			SpecificHeaders = Specific::Process,
			ExtraSdFlags = SD_FLAG_NO_DELETE;
		else if(_wcsicmp(Type, L"Thread") == 0)
			Status = OpenNtThread(&Handle, StandardAccessRequested, Oa, NameAsNumber),
			SpecificHeaders = Specific::Thread,
			ExtraSdFlags = SD_FLAG_NO_DELETE;
		else if(_wcsicmp(Type, L"Job") == 0)
			Status = NtOpenJobObject_Delayed(&Handle, StandardAccessRequested, &Oa),
			SpecificHeaders = Specific::Job,
			ExtraSdFlags = SD_FLAG_NO_DELETE;
		else if(_wcsicmp(Type, L"Timer") == 0)
			Status = NtOpenTimer(&Handle, StandardAccessRequested, &Oa),
			SpecificHeaders = Specific::QueryModify,
			ExtraSdFlags = SD_FLAG_NO_DELETE;
		else if(_wcsicmp(Type, L"Event") == 0)
			Status = NtOpenEvent(&Handle, StandardAccessRequested, &Oa),
			SpecificHeaders = Specific::QueryModify,
			ExtraSdFlags = SD_FLAG_NO_DELETE;
		else if(_wcsicmp(Type, L"Semaphore") == 0)
			Status = NtOpenSemaphore(&Handle, StandardAccessRequested, &Oa),
			SpecificHeaders = Specific::QueryModify,
			ExtraSdFlags = SD_FLAG_NO_DELETE;
		else if(_wcsicmp(Type, L"Mutant") == 0)
			Status = NtOpenMutant(&Handle, StandardAccessRequested, &Oa),
			SpecificHeaders = Specific::QueryModify,
			ExtraSdFlags = SD_FLAG_NO_DELETE;
		else if(_wcsicmp(Type, L"IoCompletion") == 0)
			Status = NtOpenIoCompletion(&Handle, StandardAccessRequested, &Oa),
			SpecificHeaders = Specific::QueryModify,
			ExtraSdFlags = SD_FLAG_NO_DELETE;
		else if(_wcsicmp(Type, L"Directory") == 0)
			Status = NtOpenDirectoryObject(&Handle, StandardAccessRequested, &Oa),
			SpecificHeaders = Specific::Directory;
		else if(_wcsicmp(Type, L"EventPair") == 0)
			Status = NtOpenEventPair(&Handle, StandardAccessRequested, &Oa),
			SpecificHeaders = ObjectTypes::Specific::NullLabels,
			ExtraSdFlags = SD_FLAG_NO_DELETE;
		else if(_wcsicmp(Type, L"SymbolicLink") == 0)
			Status = NtOpenSymbolicLinkObject(&Handle, StandardAccessRequested, &Oa),
			SpecificHeaders = Specific::Query,
			ExtraSdFlags = SD_FLAG_NO_DELETE;
		else
		{
			OpenStatus |= Module::TYPE_NOT_SUPPORTED;
			return;
		}

		RaiseOnNtError(Status);

		Res.Object(new ObjMgrObject(Handle, SpecificHeaders, ExtraSdFlags));
		OpenStatus |= Module::OBJECT_OPENED;
	}

public:
	virtual void OpenResources(Module::OPEN_STATUS &OpenStatus, SDE_GENERIC_OPTS const &GOpts, Module::ModuleResourceDest &Res, UCHAR Privileges, BOOLEAN ObjectWanted) override
	{
		__try
		{
			OpenResourcesGuarded(OpenStatus, GOpts, Res, Privileges, ObjectWanted);
		}
		__except(ExceptionToOpenStatus(exception_info(), OpenStatus))
		{}
	}
};

}//namespace Ntapi